import {encrypt, decrypt} from './TimestampEncryption';

// 存储数据元和过期数组
const expiredArr: any[] = [];
const timestampKey = 'pmatsemit', dataMapKey = 'yzjforum';
const storage = window.localStorage;

/**
 * 获取时间戳
 */
function getStorageTimestampKey() {
    let cache = new Cache();
    let timestamp;
    if ((timestamp = cache.getCookie(timestampKey)) === '') {
        timestamp = storage.getItem(timestampKey);
        if (!timestamp || timestamp === '') {
            timestamp = new Date().getTime();
            storage.setItem(timestampKey, timestamp + '');
            cache.setCookie(timestampKey, timestamp + '', undefined);
        }
    }
    return timestamp + '';
}

/**
 * 初始化 dataMap
 */
function initDataMap() {
    // 初始化 dataMap
    let saveData = {
            timestamp: ''
        },
        timestamp;
    timestamp = storage.getItem(timestampKey)
    if (!timestamp || timestamp === '') {
        // 如果时间戳不存在则生成并插入缓存中
        timestamp = getStorageTimestampKey();
    }
    saveData.timestamp = timestamp;
    storage.setItem(dataMapKey, JSON.stringify(saveData));
    return JSON.stringify(saveData);
}

/**
 * 判断是否为空
 * @param data 数据
 */
function isEmpty(data: any) {
    return !data || data === '';
}

/**
 * 判断字符串是否为 JSON
 * @param str 字符串
 */
function isJSON(str: string) {
    if (typeof str == 'string') {
        try {
            let obj = JSON.parse(str);
            return !!(typeof obj == 'object' && obj);
        } catch (e) {
            return false;
        }
    } else if (typeof str == 'object') {
        return true;
    }
}

/**
 * 存储 key 的元数据
 */
class KeyDataEntity {
    key: string;
    value: any;
    expired: any;
    func: any;

    constructor(key: string, value: any) {
        this.key = key;
        this.value = value;
    }
}

class Cache {
    getCookie(key: string) {
        key = key + '=';
        if (document.cookie) {
            let cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                let val = cookies[i].trim();
                if (val.indexOf(key) === 0) {
                    return val.substring(key.length, val.length);
                }
            }
        }
        return '';
    }

    setCookie(key: string, val: string, expired: number | null | undefined) {
        let exp = '';
        if (expired) {
            let day = new Date();
            day.setTime(day.getTime() + expired);
            exp = 'expires=' + day.toUTCString();
        }
        if (isEmpty(exp)) {
            document.cookie = key + '=' + val + ';';
        } else {
            document.cookie = key + '=' + val + ';' + exp;
        }
    }

    /**
     * set 缓存
     *
     * <ul>
     *  <li>缓存以 dataMap 作为存储，全部数据存储入一个 JSON 当中。</li>
     *  <li>dataMap 下包含各个存储的数据，当某键值过期时，则从 dataMap 中移除。</li>
     * </ul>
     * @param key 键值
     * @param val 存储数据
     * @param expired 过期时间
     * @param expiredCallback 过期函数
     */
    set(key: string, val: any, expired?: number | null | undefined, expiredCallback?: any | null | undefined) {
        let dataMap = storage.getItem(dataMapKey) as string;
        if (isEmpty(dataMap)) {
            dataMap = initDataMap();
        }
        // 插入缓存
        let dataJson = JSON.parse(dataMap);
        if (!dataJson?.timestamp) {
            // timestamp 不存在则重新初始化，所有数据失效。
            dataJson = JSON.parse(initDataMap());
        }
        if (isJSON(val)) {
            val = JSON.stringify(val);
        }
        const keyJson = new KeyDataEntity(key, val);
        if (expired) {
            // 如果存在过期时间
            keyJson.expired = new Date().getTime() + expired;
            // 存在过期回调函数且为 function
            if (expiredCallback && typeof expiredCallback == 'function') {
                // 追加定时器
                let listener = setTimeout(() => {
                    expiredCallback();
                    this.remove(key);
                }, expired);
                expiredArr.push({key: listener});
                // 加入缓存当中
                keyJson.func = expiredCallback.toString();
            }
        }
        dataJson[key] = encrypt(JSON.stringify(keyJson), dataJson.timestamp);
        // 更新 dataMap
        storage.setItem(dataMapKey, JSON.stringify(dataJson));
    }

    get(key: string) {
        let dataMap = storage.getItem(dataMapKey) as string;
        // dataMap 是否存在
        if (isEmpty(dataMap)) {
            dataMap = initDataMap();
        }
        // 插入缓存
        let dataJson = JSON.parse(dataMap);
        if (!dataJson?.timestamp) {
            // timestamp 不存在则重新初始化，使所有数据失效。
            dataJson = JSON.parse(initDataMap());
        }
        let result, expired;
        if (dataJson[key]) {
            if ((expired = dataJson[key]?.expired)) {
                if (Number(expired) < new Date().getTime()) {
                    // 缓存过期
                    this.remove(key);
                    return null;
                }
            }
            if (dataJson.timestamp) {
                let keyData = JSON.parse(decrypt(dataJson[key], dataJson.timestamp) as string);
                if (isJSON((result = keyData.value))) {
                    return JSON.parse(result);
                }
            }
        }
        return result;
    }

    removes(keys: string[]) {
        for (let key of keys) {
            this.remove(key);
        }
    }

    remove(key: string) {
        let dataMap = storage.getItem(dataMapKey), keyData;
        if (dataMap) {
            let dataJson = JSON.parse(dataMap);
            if ((keyData = dataJson[key])) {
                let keyJson = JSON.parse(decrypt(keyData, dataJson.timestamp) as string);
                if (keyJson?.func) {
                    let func = Function('"use strict";return (' + keyJson.func + ')')();
                    func();
                }
                delete dataJson[key];
                storage.setItem(dataMapKey, JSON.stringify(dataJson));
            }
        }
        // 删除 expiredArr
        if (expiredArr.length > 0) {
            let index = 0;
            for (let expired of expiredArr) {
                if (expired[key]) {
                    expiredArr.splice(index, 1);
                }
                index++;
            }
        }
    }
}

// 遍历监听过期，并执行过期函数
setInterval(() => {
    let cache = new Cache();
    // 执行缓存中的过期函数
    let dataMap = storage.getItem(dataMapKey), json;
    if (dataMap && dataMap !== '') {
        json = JSON.parse(dataMap);
        for (let jsonKey in json) {
            if (jsonKey !== 'timestamp') {
                let obj = decrypt(json[jsonKey], json.timestamp);
                if (obj) {
                    let objJson = JSON.parse(obj), expired;
                    if ((expired = objJson?.expired) && expired < new Date().getTime()) {
                        cache.remove(jsonKey);
                    }
                }
            }
        }
    }
}, 1000);

export default Cache;